package ij3d.geom;

import ij3d.utils.*;
import ij.*;
import ij.measure.*;
import ij.plugin.filter.*;
import ij.process.*;
import ij.gui.Roi;

import ij3d.image3d.*;
import ij3d.Jama.*;

import java.awt.Color;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;

/**
 *  An Object3D in an image
 *
 * @author          thomas & Cedric
 * @created         17 septembre 2003
 * @lastmodified    27 octobre 2003
 */
public class Object3D {

    /**
     *  Bounding box
     */
    protected int xmin, ymin, zmin, xmax, ymax, zmax;
    /**
     *  IsoBarycenter x
     */
    protected double bx = Double.NaN;
    /**
     * IsoBarycenter x
     */
    protected double by = Double.NaN;
    /**
     * IsoBarycenter y
     */
    protected double bz = Double.NaN;
    /**
     *  Center of mass x
     */
    protected double cx = Double.NaN;
    /**
     * Center of mass y
     */
    protected double cy = Double.NaN;
    /**
     * Center of mass z
     */
    protected double cz = Double.NaN;
    /**
     *  Area
     */
    protected int area = -1;
    /**
     *  volume
     */
    protected int volume = -1;
    /**
     *  feret diameter
     */
    protected double feret = Double.NaN;
    /**
     * Pixel 1 for feret diameter
     */
    protected Pixel3D feret1 = null;
    /**
     * Pixel 2 for feret diameter
     */
    protected Pixel3D feret2 = null;
    // center distances stat
    double distcentermin = Double.NaN;
    double distcentermax = Double.NaN;
    double distcentermean = Double.NaN;
    double distcentersigma = Double.NaN;
    /**
     *  Integrated density (sum of pixels)
     */
    protected double integratedDensity = Double.NaN;    
    /**
     * standard deviation
     */
    protected double sigma = Double.NaN;
    /**
     *  Min pix value in object
     */
    protected double pixmin = Double.NaN;
    /**
     *  Max pix value in object
     */
    protected double pixmax = Double.NaN;
    /**
     *  Value (grey-level)
     */
    protected int value;
    /**
     *  Contours pixels
     */
    protected ArrayList contours = null;
    /**
     *  Touch the borders ?
     */
    protected boolean touchBorders;
    /**
     *  Centred Moments
     */
    protected double sxx = Double.NaN;
    /**
     *  Centred Moments
     */
    protected double sxy, sxz, syy, syz, szz;
    /**
     *  Matrix of decomposition
     */
    protected EigenvalueDecomposition eigen = null;
    /**
     *  The image where the object lies
     */
    protected IntImage3D segImage;

    // the current image for quantification
    private Image3D currentQuantifImage = null;
    /**
     *  the resolution of the image and then object
     */
    double resXY;
    double resZ;
    String units;

    // debug
    private boolean debug = false;
    /**
     *  The ArrayList of voxels for Object
     */
    private ArrayList alObject;

    /**
     *  constructeur
     *
     * @param  ima  Segmented iamge
     * @param  val  Pixel value of the object
     */
    public Object3D(IntImage3D ima, int val) {
        init();

        value = val;
        segImage = ima;
        resXY = 1.0;
        resZ = 1.0;
        units = "pix";
    }

    /**
     *  Constructor for the Object3D object
     *
     * @param  plus  Segmented image
     * @param  val   Pixel value of the object
     */
    public Object3D(ImagePlus plus, int val) {
        init();

        value = val;
        IntImage3D ima = new IntImage3D(plus.getStack());
        segImage = ima;

        // Calibration
        Calibration cal = plus.getCalibration();
        if (cal != null) {
            if (cal.scaled()) {
                resXY = cal.getX(1.0);
                resZ = cal.getZ(1.0);
                units = cal.getUnits();
            } else {
                resXY = 1.0;
                resZ = 1.0;
                units = "pix";
            }
        }
        //System.out.println("res " + resXY + " " + resZ + " " + units);
        this.computeBounding();
        this.computeCenter();
    }

    /**
     * copy
     * @param other
     */
    public Object3D(Object3D other) {
        init();
        value = 0;
        this.segImage = other.getSegImage();
        this.setResolution(other.getResXY(), other.getResZ(), other.getUnits());
        this.computeBounding();
        this.computeCenter();
        //this.computeContours();
    }

    /**
     *  Init default values
     */
    private void init() {
        // center of mass
        cx = Double.NaN;
        cy = Double.NaN;
        cz = Double.NaN;
        // barycenter
        bx = Double.NaN;
        by = Double.NaN;
        bz = Double.NaN;
        // moments
        sxx = Double.NaN;
        sxy = Double.NaN;
        sxz = Double.NaN;
        syy = Double.NaN;
        syz = Double.NaN;
        szz = Double.NaN;
        
        alObject=new ArrayList<VoxelObject>();
    }

    /**
     *  Sets the resXY attribute of the Object3D object
     *
     * @param  rxy  The new resXY value
     */
    public void setResXY(double rxy) {
        resXY = rxy;
    }

    /**
     *  Sets the resZ attribute of the Object3D object
     *
     * @param  rz  The new resZ value
     */
    public void setResZ(double rz) {
        resZ = rz;
    }

    /**
     *  Sets the units attribute of the Object3D object
     *
     * @param  u  The new units value
     */
    public void setUnits(String u) {
        units = u;
    }

    /**
     *  Gets the resXY attribute of the Object3D object
     *
     * @return    The resXY value
     */
    public double getResXY() {
        return resXY;
    }

    /**
     *  Gets the resZ attribute of the Object3D object
     *
     * @return    The resZ value
     */
    public double getResZ() {
        return resZ;
    }

    /**
     *  Gets the unit attribute of the Object3D object
     *
     * @return    The unit value
     */
    public String getUnits() {
        return units;
    }

    /**
     *  Gets the calibration attribute of the Object3D object
     *
     * @return    The calibration value
     */
    public Calibration getCalibration() {
        Calibration cal = new Calibration();
        cal.pixelWidth = resXY;
        cal.pixelHeight = resXY;
        cal.pixelDepth = resZ;
        cal.setUnit(units);

        return cal;
    }

    /**
     *  Sets the calibration attribute of the Object3D object
     *
     * @param  cal  The new calibration value
     */
    public void setCalibration(Calibration cal) {
        resXY = cal.pixelWidth;
        resZ = cal.pixelDepth;
        units = cal.getUnits();
    }

    /**
     *  Sets the resolution attribute of the ObjectCreator3D object
     *
     * @param  rxy  The new resolution in XY
     * @param  rz   The new resolution in Z
     * @param  u    The new resolution unit
     */
    public final void setResolution(double rxy, double rz, String u) {
        resXY = rxy;
        resZ = rz;
        units = u;
    }

    /**
     *  the pixel is contour ?
     *
     * @param  x  x coordinate
     * @param  y  y coordinate
     * @param  z  z coordinate
     * @return   true if pixel is contour
     */
    protected boolean isContour(int x, int y, int z) {
        if (segImage.getPixel(x, y, z) != value) {
            return false;
        }
        ArrayUtil vois = segImage.getNeighborhood3(x, y, z);
        // different from its own value
        if (!vois.hasOnlyValue(value)) {
            return true;
        }
        return false;
    }

    /**
     *  volume of the Object3D (in pixel)
     *
     * @return    the volume value
     */
    public int getVolumePixels() {
        if (volume == -1) {
            computeCenter();
        }
        return volume;
    }

    /**
     *  Gets the volumeUnit attribute of the Object3D object
     *
     * @return    The volumeUnit value
     */
    public double getVolumeUnit() {
        return getVolumePixels() * resXY * resXY * resZ;
    }

    /**
     *  integrated density of the Object3D
     *
     * @param ima
     * @return    the sum of pixels values
     */
    public double getIntegratedDensity(Image3D ima) {
        if ((currentQuantifImage == null) || (currentQuantifImage != ima)) {
            computeMassCenter(ima);
            currentQuantifImage = ima;
        }
        return integratedDensity;
    }

    /**
     *  Gets the compacite attribute of the Object3D (unit)
     *
     * @return    The compacite value
     */
    public double getCompacite() {
        double s3 = Math.pow(getAreaUnit(), 3);
        double v2 = Math.pow(getVolumeUnit(), 2);
        return (v2 * 36.0 * Math.PI) / s3;
    }

    /**
     *  Gets the ration between volume
     * and volume of the bounding box
     * (in units)
     *
     * @return    The ratioBox value
     */
    public double getRatioBox() {
        double vol = getVolumeUnit();
        if (zmax == -1) {
            computeBounding();
        }
        double volbox = (zmax - zmin) * (xmax - xmin) * (ymax - ymin) * resZ * resXY * resXY;

        return vol / volbox;
    }

    /**
     *  Gets the feret attribute of the Object3D object (unit)
     */
    private void computeFeret() {
        double distmax = 0;
        double dist;
        double rx2 = resXY * resXY;
        double rz2 = resZ * resZ;
        Pixel3D p1;
        Pixel3D p2;
        if (contours == null) {
            computeContours();
        }
        int s = contours.size();
        IJ.log("Feret contour size =" + s);
        for (int i = 0; i < s; i++) {
            IJ.showStatus("Feret " + (100 * i / s) + " % ");
            p1 = (Pixel3D) contours.get(i);
            for (int j = i + 1; j < s; j++) {
                p2 = (Pixel3D) contours.get(j);
                //IJ.log("i=" + i + " j=" + j + " p1=" + p1 + " +p2=" + p2);
                dist = rx2 * ((p1.getX() - p2.getX()) * (p1.getX() - p2.getX()) + ((p1.getY() - p2.getY()) * (p1.getY() - p2.getY()))) + rz2 * (p1.getZ() - p2.getZ()) * (p1.getZ() - p2.getZ());
                if (dist > distmax) {
                    distmax = dist;
                    feret1 = p1;
                    feret2 = p2;
                }
            }
        }
        feret = (float) Math.sqrt(distmax);
    }

    private void computeDistCenter() {
        double dist2 = 0;
        double distmax = 0;
        double distmin = Double.POSITIVE_INFINITY;
        double distsum = 0;
        double distsum2 = 0;
        double rx2 = resXY * resXY;
        double rz2 = resZ * resZ;
        Vector3D vc = getCenter();
        Pixel3D center = new Pixel3D(vc.getX(), vc.getY(), vc.getZ(), 0);
        double ccx = center.getX();
        double ccy = center.getY();
        double ccz = center.getZ();
        Pixel3D p2;

        if (contours == null) {
            computeContours();
        }
        int s = contours.size();
        double sd = s;
        Pixel3D tmpmin = null;

        for (int j = 0; j < s; j++) {
            p2 = (Pixel3D) contours.get(j);
            dist2 = rx2 * ((ccx - p2.getX()) * (ccx - p2.getX()) + ((ccy - p2.getY()) * (ccy - p2.getY()))) + rz2 * (ccz - p2.getZ()) * (ccz - p2.getZ());
            distsum += Math.sqrt(dist2);
            distsum2 += dist2;
            if (dist2 > distmax) {
                distmax = dist2;
            }
            if (dist2 < distmin) {
                distmin = dist2;
                tmpmin = p2;
                //System.out.println("pix=" + tmpmin + " dist=" + distmin);
            }

        }

        distcentermax = Math.sqrt(distmax);
        distcentermin = Math.sqrt(distmin);
        distcentermean = distsum / sd;
        distcentersigma = Math.sqrt((distsum2 - ((distsum * distsum) / sd)) / (sd - 1));
    }

    /**
     *  Gets the feret attribute of the Object3D object (unit)
     *
     * @return    The feret value
     */
    public double getFeret() {
        if (Double.isNaN(feret)) {
            computeFeret();
        }

        return feret;
    }

    /**
     *
     * @return
     */
    public Pixel3D getFeretPixel1() {
        if (feret1 == null) {
            computeFeret();
        }
        return feret1;
    }

    /**
     *
     * @return
     */
    public Pixel3D getFeretPixel2() {
        if (feret2 == null) {
            computeFeret();
        }
        return feret2;
    }

    /**
     *
     * @return
     */
    public double getDistCenterMin() {
        if (Double.isNaN(distcentermin)) {
            this.computeDistCenter();
        }

        return distcentermin;
    }

    /**
     *
     * @return
     */
    public double getDistCenterMax() {
        if (Double.isNaN(distcentermax)) {
            this.computeDistCenter();
        }

        return distcentermax;
    }

    /**
     *
     * @return
     */
    public double getDistCenterMean() {
        if (Double.isNaN(distcentermean)) {
            this.computeDistCenter();
        }

        return distcentermean;
    }

    /**
     *
     * @return
     */
    public double getDistCenterSigma() {
        if (Double.isNaN(distcentersigma)) {
            this.computeDistCenter();
        }

        return distcentersigma;
    }

    /**
     *  the area of the Object3D (in pixels)
     *
     * @return    the area value
     */
    public int getAreaPixels() {
        if (area == -1) {
            computeContours();
        }
        return area;
    }

    /**
     *  Gets the areaUnit attribute of the Object3D object
     *
     * @return    The areaUnit value
     */
    public double getAreaUnit() {
        // TODO real surface vlaues, orientation of pixel borders
        return getAreaPixels() * resXY * resZ;
    }

    /**
     *  Gets the center attribute of the Object3D object
     *
     * @return    The center value
     */
    public Vector3D getCenter() {
        if (Double.isNaN(bx)) {
            computeCenter();
        }
        return new Vector3D(bx, by, bz);
    }

    /**
     *  Gets the centerUnit attribute of the Object3D object
     *
     * @return    The centerUnit value
     */
    public Vector3D getCenterUnit() {
        Vector3D ce = getCenter();
        return new Vector3D(ce.getX() * resXY, ce.getY() * resXY, ce.getZ() * resZ);
    }

    /**
     *  x coordinate of the center
     *
     * @return    x center value
     */
    public double getCenterX() {
        Vector3D ce = getCenter();
        return ce.getX();
    }

    /**
     *  y coordinate of the center
     *
     * @return    y center value
     */
    public double getCenterY() {
        Vector3D ce = getCenter();
        return ce.getY();
    }

    /**
     *  z coordinate of the center
     *
     * @return    z center value
     */
    public double getCenterZ() {
        Vector3D ce = getCenter();
        return ce.getZ();
    }

    /**
     *  x coordinate of the center
     *
     * @param ima
     * @return    x center value
     */
    public double getMassCenterX(Image3D ima) {
        if ((currentQuantifImage == null) || (currentQuantifImage != ima)) {
            computeMassCenter(ima);
            currentQuantifImage = ima;
        }
        return cx;
    }

    /**
     *  y coordinate of the center
     *
     * @param ima
     * @return    y center value
     */
    public double getMassCenterY(Image3D ima) {
        if ((currentQuantifImage == null) || (currentQuantifImage != ima)) {
            computeMassCenter(ima);
            currentQuantifImage = ima;
        }
        return cy;
    }

    /**
     *  z coordinate of the center
     *
     * @param ima
     * @return    z center value
     */
    public double getMassCenterZ(Image3D ima) {
        if ((currentQuantifImage == null) || (currentQuantifImage != ima)) {
            computeMassCenter(ima);
            currentQuantifImage = ima;
        }
        return cz;
    }

    /**
     *  Gets the meanPixValue attribute of the Object3D object
     *
     * @param ima
     * @return    The meanPixValue value
     */
    public double getMeanPixValue(Image3D ima) {
        return (getIntegratedDensity(ima) / getVolumePixels());
    }

    /**
     *  Gets the SD of pixels in object
     *
     * @param ima the 3D image
     * @return    The sigma value
     */
    public double getSigmaPixValue(Image3D ima) {
        if ((currentQuantifImage == null) || (currentQuantifImage != ima)) {
            computeMassCenter(ima);
            currentQuantifImage = ima;
        }
        return (sigma);
    }

    /**
     *  Gets the pixMinValue attribute of the Object3D object
     *
     * @param ima
     * @return    The pixMinValue value
     */
    public double getPixMinValue(Image3D ima) {
        if ((currentQuantifImage == null) || (currentQuantifImage != ima)) {
            computeMassCenter(ima);
            currentQuantifImage = ima;
        }
        return pixmin;
    }

    /**
     *  Gets the pixMaxValue attribute of the Object3D object
     *
     * @param ima
     * @return    The pixMaxValue value
     */
    public double getPixMaxValue(Image3D ima) {
        if ((currentQuantifImage == null) || (currentQuantifImage != ima)) {
            computeMassCenter(ima);
            currentQuantifImage = ima;
        }
        return pixmax;
    }

    /**
     *
     * @param ima
     * @return
     */
    public Pixel3D getPixelMax(Image3D ima) {
        Pixel3D res = null;
        float pix;
        float max = Float.MIN_VALUE;

        for (int k = zmin; k <= zmax; k++) {
            for (int j = ymin; j <= ymax; j++) {
                for (int i = xmin; i <= xmax; i++) {
                    if (segImage.getPixel(i, j, k) == value) {
                        pix = ima.getPix(i, j, k);
                        if (pix > max) {
                            max = pix;
                            res = new Pixel3D(i, j, k, pix);
                        }
                    }
                }
            }
        }

        return res;
    }

    /**
     *  Gets the xmin attribute of the Object3D
     *
     * @return    The xmin value
     */
    public int getXmin() {
        if (xmin == -1) {
            computeBounding();
        }
        return xmin;
    }

    /**
     *  Gets the ymin attribute of the Object3D
     *
     * @return    The ymin value
     */
    public int getYmin() {
        if (ymin == -1) {
            computeBounding();
        }
        return ymin;
    }

    /**
     *  Gets the zmin attribute of the Object3D
     *
     * @return    The zmin value
     */
    public int getZmin() {
        if (zmin == -1) {
            computeBounding();
        }
        return zmin;
    }

    /**
     *  Gets the xmax attribute of the Object3D
     *
     * @return    The xmax value
     */
    public int getXmax() {
        if (xmax == -1) {
            computeBounding();
        }
        return xmax;
    }

    /**
     *  Gets the ymax attribute of the Object3D
     *
     * @return    The ymax value
     */
    public int getYmax() {
        if (ymax == -1) {
            computeBounding();
        }
        return ymax;
    }

    /**
     *  Gets the zmax attribute of the Object3D
     *
     * @return    The zmax value
     */
    public int getZmax() {
        if (zmax == -1) {
            computeBounding();
        }
        return zmax;
    }

    /**
     *  value of the Object3D (in gray level)
     *
     * @return    value
     */
    public int getValue() {
        return value;
    }

    /**
     *  Sets the value attribute of the Object3D object
     *
     * @param  v  The new value value
     */
    public void setValue(int v) {
        value = v;
    }

    /**
     *  Gets the segImage attribute of the Object3D object
     *
     * @return    The segImage value
     */
    public IntImage3D getSegImage() {
        return segImage;
    }

    /**
     *  Gets the touchBorders attribute of the FishObject3D Object3D
     *
     * @return    The touchBorders value
     */
    public boolean getTouchBorders() {
        return touchBorders;
    }

    /**
     *  2D distance from center to center (in real distance)
     *
     * @param  autre  the other Object3D
     * @return        distance
     */
    public double distCenter2D(Object3D autre) {
        double dist = Math.sqrt((bx - autre.bx) * (bx - autre.bx) * resXY * resXY + (by - autre.by) * (by - autre.by) * resXY * resXY);
        return dist;
    }

    /**
     *  distance from center to center (in real distance)
     *
     * @param  autre  the other Object3D (has the same resolution !)
     * @return        distance
     */
    public double distCenter(Object3D autre) {
        double dist = Math.sqrt((bx - autre.bx) * (bx - autre.bx) * resXY * resXY + (by - autre.by) * (by - autre.by) * resXY * resXY + (bz - autre.bz) * (bz - autre.bz) * resZ * resZ);
        return dist;
    }

    /**
     *  Angle between (a, this, b) with real coordinates
     *
     * @param  a  Object3D a
     * @param  b  Object3D b
     * @return    Angle in degrees
     */
    public double angle(Object3D a, Object3D b) {
        double OI = this.distCenter(a);
        double OF = this.distCenter(b);
        double IF = a.distCenter(b);
        double cosangle = (IF * IF - OF * OF - OI * OI) / (-2.0 * OF * OI);
        double angledeg = Math.toDegrees(Math.acos(cosangle));
        return (angledeg);
    }

    /**
     *  the minimum distance between two objets (in real distance) computed on the
     *  contours pixels
     *
     * @param  autre  the other Object3D
     * @return        the minimum distance
     */
    public double distBorder(Object3D autre) {
        return vectorBorderBorder(autre).getLength(resXY, resZ);
    }

    /**
     *  The vector between the two closest border to border point
     *
     * @param  autre  the other object
     * @return        The difference vector between the two closest points
     */
    public Vector3D vectorBorderBorder0(Object3D autre) {
        double dist = -1;
        double distmin;
        int s = contours.size();
        ArrayList autrecontours = autre.contours;
        int t = autrecontours.size();
        Pixel3D p0 = (Pixel3D) contours.get(0);
        Pixel3D p1 = (Pixel3D) autrecontours.get(0);
        Pixel3D ici = p0;
        Pixel3D ailleur = p1;

        double resXY2 = resXY * resXY;
        double resZ2 = resZ * resZ;

        distmin = (ici.getX() - ailleur.getX()) * (ici.getX() - ailleur.getX()) * resXY2 + (ici.getY() - ailleur.getY()) * (ici.getY() - ailleur.getY()) * resXY2 + (ici.getZ() - ailleur.getZ()) * (ici.getZ() - ailleur.getZ()) * resZ2;


        for (int i = 0; i < s; i++) {
            IJ.showStatus("Computing distance " + (100 * i) / s + "%");
            ici = (Pixel3D) contours.get(i);
            for (int j = 0; j < t; j++) {
                ailleur = (Pixel3D) autrecontours.get(j);
                dist = (ici.getX() - ailleur.getX()) * (ici.getX() - ailleur.getX()) * resXY2 + (ici.getY() - ailleur.getY()) * (ici.getY() - ailleur.getY()) * resXY2 + (ici.getZ() - ailleur.getZ()) * (ici.getZ() - ailleur.getZ()) * resZ2;
                if (dist < distmin) {
                    distmin = dist;
                    p0 = ici;
                    p1 = ailleur;
                }
            }
        }

        IJ.showStatus("Computing distance 100%");
        return new Vector3D((p1.getX() - p0.getX()), (p1.getY() - p0.getY()), (p1.getZ() - p0.getZ()));
    }

    /**
     * The vector between two borders
     * @param other
     * @return the vector of minimal distance between two objects
     */
    public Vector3D vectorBorderBorder(Object3D other) {
        double distmin;
        final int s = contours.size();
        final ArrayList autrecontours = other.contours;
        final int t = autrecontours.size();
        final Pixel3D p0 = (Pixel3D) contours.get(0);
        final Pixel3D p1 = (Pixel3D) autrecontours.get(0);
        Pixel3D ici = p0;
        Pixel3D ailleur = p1;

        final double resXY2 = resXY * resXY;
        final double resZ2 = resZ * resZ;

        distmin = (ici.getX() - ailleur.getX()) * (ici.getX() - ailleur.getX()) * resXY2 + (ici.getY() - ailleur.getY()) * (ici.getY() - ailleur.getY()) * resXY2 + (ici.getZ() - ailleur.getZ()) * (ici.getZ() - ailleur.getZ()) * resZ2;

        // PARALLEL (Thomas Boudier)
        final AtomicInteger ai = new AtomicInteger(0);
        Thread[] threads = ThreadUtil.createThreadArray();
        final int n_cpus = ThreadUtil.getNbCpus();

        final double[] disttab = new double[n_cpus];
        final Pixel3D[] p0tab = new Pixel3D[n_cpus];
        final Pixel3D[] p1tab = new Pixel3D[n_cpus];

        for (int i = 0; i < n_cpus; i++) {
            disttab[i] = distmin;
            p0tab[i] = p0;
            p1tab[i] = p1;
        }

        for (int ithread = 0; ithread < threads.length; ithread++) {
            threads[ithread] = new Thread() {

                @Override
                public void run() {
                    Pixel3D ici2 = p0;
                    Pixel3D ailleur2 = p1;
                    double dist2 = -1;
                    float sf = (float) s;

                    for (int cpu = ai.getAndIncrement(); cpu < n_cpus; cpu = ai.getAndIncrement()) {
                        for (int i = cpu; i < s; i += n_cpus) {
                            IJ.showStatus("Computing distance : " + (100 * i) / s + "%");
                            ici2 = (Pixel3D) contours.get(i);
                            for (Iterator ite = autrecontours.iterator(); ite.hasNext();) {
                                ailleur2 = (Pixel3D) ite.next();
                                dist2 = (ici2.getX() - ailleur2.getX()) * (ici2.getX() - ailleur2.getX()) * resXY2 + (ici2.getY() - ailleur2.getY()) * (ici2.getY() - ailleur2.getY()) * resXY2 + (ici2.getZ() - ailleur2.getZ()) * (ici2.getZ() - ailleur2.getZ()) * resZ2;
                                if (dist2 < disttab[cpu]) {
                                    disttab[cpu] = dist2;
                                    p0tab[cpu] = ici2;
                                    p1tab[cpu] = ailleur2;
                                }
                            }
                        }
                    }
                }
            };
        }

        ThreadUtil.startAndJoin(threads);

        // compare all cpus
        distmin = disttab[0];
        Pixel3D p00 = p0tab[0];
        Pixel3D p11 = p1tab[0];
        for (int i = 1; i < n_cpus; i++) {
            if (disttab[i] < distmin) {
                distmin = disttab[i];
                p00 = p0tab[i];
                p11 = p1tab[i];
            }
        }

        IJ.showStatus("Computing distance 100%");
        return new Vector3D((p11.getX() - p00.getX()), (p11.getY() - p00.getY()), (p11.getZ() - p00.getZ()));
    }

    /**
     *  The vector between the center and border point
     *
     * @param  autre  the othe object
     * @return        The difference vector between the two closest points
     */
    public Vector3D vectorCenterBorder(Object3D autre) {
        double distmin = -1;
        double dist;
        Pixel3D p1 = new Pixel3D();
        Pixel3D ailleur = null;
        ArrayList autrecontour = autre.contours;

        int s = autrecontour.size();
        int c = 0;
        for (int i = 0; i < s; i++) {
            IJ.showStatus("Computing distance " + ((100 * c) / s) + "%");
            ailleur = (Pixel3D) autrecontour.get(i);
            dist = (bx - ailleur.getX()) * (bx - ailleur.getX()) * resXY * resXY + (by - ailleur.getY()) * (by - ailleur.getY()) * resXY * resXY + (bz - ailleur.getZ()) * (bz - ailleur.getZ()) * resZ * resZ;
            if (distmin == -1) {
                distmin = dist;
                p1 = ailleur;
            } else if (dist < distmin) {
                distmin = dist;
                p1 = ailleur;
            }
            c++;
        }
        IJ.showStatus("Computing distance 100%");
        return new Vector3D((-bx + p1.getX()), (-by + p1.getY()), (-bz + p1.getZ()));
    }

    /**
     *  Distance center to the border
     *
     * @param  autre  Other object
     * @return        Min distance between center and border ot other object
     */
    public double distCenterBorder(Object3D autre) {
        return vectorCenterBorder(autre).getLength(resXY, resZ);
    }

    /**
     *  Distance from a point to the border of the object
     *
     * @param  x  x coordinate of the point
     * @param  y  y coordinate of the point
     * @param  z  z coordinate of the point
     * @return    The shortest distance
     */
    public double distPixelBorder(double x, double y, double z) {
        return vectorPixelBorder(x, y, z).getLength(resXY, resZ);
    }

    /**
     *
     * @param x
     * @param y
     * @param z
     * @return
     */
    public double distPixelCenter(double x, double y, double z) {
        return this.getCenter().distance(new Vector3D(x, y, z));
    }

    /**
     *
     * @param P
     * @return
     */
    public double distPixelBorder(Point3D P) {
        return vectorPixelBorder(P.getX(), P.getY(), P.getZ()).getLength(resXY, resZ);
    }

    /**
     *  Description of the Method
     *
     * @param  x  Description of the Parameter
     * @param  y  Description of the Parameter
     * @param  z  Description of the Parameter
     * @return    Description of the Return Value
     */
    public Vector3D vectorPixelUnitBorder(double x, double y, double z) {
        return vectorPixelBorder(x / resXY, y / resXY, z / resZ);
    }

    /**
     *  Description of the Method
     *
     * @param  x    Description of the Parameter
     * @param  y    Description of the Parameter
     * @param  z    Description of the Parameter
     * @param  dir  Description of the Parameter
     * @return      Description of the Return Value
     */
    public Vector3D vectorPixelUnitBorder(double x, double y, double z, Vector3D dir) {
        Vector3D dir2 = dir.multiply(1.0 / resXY, 1.0 / resXY, 1.0 / resZ);
        return vectorPixelBorder(x / resXY, y / resXY, z / resZ, dir2);
    }

    /**
     *  Vector between a pixel and the closest border
     *
     * @param  x  x coordinate of the pixel
     * @param  y  y coordinate of the pixel
     * @param  z  z coordinate of the pixel
     * @return    the vector
     */
    public Vector3D vectorPixelBorder(double x, double y, double z) {
        double distmin = Double.MAX_VALUE;
        double dist;
        double rx2 = resXY * resXY;
        double rz2 = resZ * resZ;
        Pixel3D here;
        Pixel3D stock = new Pixel3D();
        int s = contours.size();
        for (int i = 0; i < s; i++) {
            here = (Pixel3D) contours.get(i);
            dist = rx2 * ((x - here.getX()) * (x - here.getX()) + (y - here.getY()) * (y - here.getY())) + (z - here.getZ()) * (z - here.getZ()) * rz2;
            if (dist < distmin) {
                distmin = dist;
                stock = here;
            }
        }
        return new Vector3D(stock.getX() - x, stock.getY() - y, stock.getZ() - z);
    }

    /**
     *  Vector between a pixel and the closest border
     *
     * @param  V  Description of the Parameter
     * @return    the vector
     */
    public Vector3D vectorPixelBorder(Vector3D V) {
        return vectorPixelBorder(V.getX(), V.getY(), V.getZ());
    }

    /**
     *  Description of the Method
     *
     * @param  V  Description of the Parameter
     * @return    Description of the Return Value
     */
    public Vector3D vectorPixelUnitBorder(Vector3D V) {
        return vectorPixelUnitBorder(V.getX(), V.getY(), V.getZ());
    }

    /**
     *  Description of the Method
     *
     * @param  V    Description of the Parameter
     * @param  dir  Description of the Parameter
     * @return      Description of the Return Value
     */
    public Vector3D vectorPixelUnitBorder(Vector3D V, Vector3D dir) {
        return vectorPixelUnitBorder(V.getX(), V.getY(), V.getZ(), dir);
    }

    /**
     *  Distance from a point along a direction to the border of the object
     *
     * @param  x  x coordinate of the point
     * @param  y  y coordinate of the point
     * @param  z  z coordinate of the point
     * @param  V  the direction vector
     * @return    The shortest distance
     */
    public double distPixelBorder(double x, double y, double z, Vector3D V) {
        return vectorPixelBorder(x, y, z, V).getLength(resXY, resZ);
    }

    /**
     * @param  x
     * @param  y
     * @param  z
     * @return
     */
    public double radiusPixel(double x, double y, double z) {
        Vector3D V = getCenter().add(new Vector3D(x, y, z), -1, 1);
        return distPixelBorder(getCenterX(), getCenterY(), getCenterZ(), V);
    }

    /**
     * @param  V
     * @return
     */
    public double radiusCenter(Vector3D V) {
        Vector3D VV = getCenter().add(V, -1, 1);
        return distPixelBorder(getCenterX(), getCenterY(), getCenterZ(), VV);
    }

    /**
     * @param  obj
     * @return
     */
    public double radiusCenter(Object3D obj) {
        Vector3D VV = getCenter().add(obj.getCenter(), -1, 1);
        return distPixelBorder(getCenterX(), getCenterY(), getCenterZ(), VV);
    }

    /**
     *  Vector from a point along a direction to the border of the object
     *
     * @param  x  x coordinate of the point
     * @param  y  y coordinate of the point
     * @param  z  z coordinate of the point
     * @param  V  the direction vector
     * @return    The shortest distance Vector
     */
    public Vector3D vectorPixelBorder(double x, double y, double z, Vector3D V) {
        double dist = 0;
        double distmin = Double.MAX_VALUE;

        Vector3D dir = new Vector3D(V);
        dir.normalize();
        double vx = dir.getX();
        double vy = dir.getY();
        double vz = dir.getZ();

        double px;
        double py;
        double pz;
        double pxmin = x;
        double pymin = y;
        double pzmin = z;
        px = x;
        py = y;
        pz = z;

        Vector3D distVect;

        while (insideImage(px, py, pz)) {
            distVect = vectorPixelBorder(px, py, pz);
            dist = distVect.getLength(resXY, resZ);
            if (dist < distmin) {
                distmin = dist;
                pxmin = px + distVect.getX();
                pymin = py + distVect.getY();
                pzmin = pz + distVect.getZ();
            }
            px += 0.5 * vx;
            py += 0.5 * vy;
            pz += 0.5 * vz;
        }

        return new Vector3D(pxmin - x, pymin - y, pzmin - z);
    }

    /**
     *  test if the point is inside the bounding box of the object
     *
     * @param  x  x coordinate of the point
     * @param  y  y coordinate of the point
     * @param  z  z coordinate of the point
     * @return    true or false
     */
    public boolean insideBounding(float x, float y, float z) {
        if ((x >= xmin) && (x <= xmax) && (y >= ymin) && (y <= ymax) && (z >= zmin) && (z <= zmax)) {
            return true;
        } else {
            return false;
        }
    }

    /**
     *  test if the point is inside the bounding box of the object
     *
     * @param  x  x coordinate of the point
     * @param  y  y coordinate of the point
     * @param  z  z coordinate of the point
     * @return    true or false
     */
    public boolean insideBounding(double x, double y, double z) {
        if ((x >= xmin) && (x <= xmax) && (y >= ymin) && (y <= ymax) && (z >= zmin) && (z <= zmax)) {
            return true;
        } else {
            return false;
        }
    }

    /**
     *  test if the point is inside the segmented image
     *
     * @param  x  x coordinate of the point
     * @param  y  y coordinate of the point
     * @param  z  z coordinate of the point
     * @return    true or false
     */
    private boolean insideImage(double x, double y, double z) {
        if ((x >= 0) && (x < segImage.getSizex()) && (y >= 0) && (y < segImage.getSizey()) && (z >= 0) && (z < segImage.getSizez())) {
            return true;
        } else {
            return false;
        }
    }

    /**
     *  Test if the bouding boxes intersect
     *
     * @param  autre  the othe object
     * @return        true or false
     */
    public boolean intersectionBox(Object3D autre) {
        int oxmin = autre.getXmin();
        int oxmax = autre.getXmax();
        int oymin = autre.getYmin();
        int oymax = autre.getYmax();
        int ozmin = autre.getZmin();
        int ozmax = autre.getZmax();

        if (insideBounding(oxmin, oymin, ozmin) || insideBounding(oxmin, oymax, ozmin) || insideBounding(oxmax, oymin, ozmin) || insideBounding(oxmax, oymax, ozmin) || insideBounding(oxmin, oymin, ozmax) || insideBounding(oxmin, oymax, ozmax) || insideBounding(oxmax, oymin, ozmax) || insideBounding(oxmax, oymax, ozmax)) {
            return true;
        } else {
            return false;
        }
    }

    /**
     * @param  other object
     * @return percentage of colocalisation of this object into the other object
     */
    public double pcColoc(Object3D other) {
        int count = 0;
        int xmin0;
        int ymin0;
        int zmin0;
        int xmax0;
        int ymax0;
        int zmax0;

        int val = other.getValue();
        IntImage3D otherseg = other.getSegImage();

        xmin0 = getXmin();
        ymin0 = getYmin();
        zmin0 = getZmin();
        xmax0 = getXmax();
        ymax0 = getYmax();
        zmax0 = getZmax();

        if (other != null) {
            xmin0 = Math.max(xmin0, other.getXmin());
            ymin0 = Math.max(ymin0, other.getYmin());
            zmin0 = Math.max(zmin0, other.getZmin());
            xmax0 = Math.min(xmax0, other.getXmax());
            ymax0 = Math.min(ymax0, other.getYmax());
            zmax0 = Math.min(zmax0, other.getZmax());
        }

        for (int k = zmin0; k <= zmax0; k++) {
            for (int j = ymin0; j <= ymax0; j++) {
                for (int i = xmin0; i <= xmax0; i++) {
                    if ((segImage.getPixel(i, j, k) == value) && (otherseg.getPixel(i, j, k) == val)) {
                        count++;
                    }
                }
            }
        }

        return 100.0 * (double) count / (double) volume;
    }

    /**
     *
     * @param ima
     * @return
     */
    public ArrayList listVoxels(Image3D ima) {
        ArrayList vector = new ArrayList();
        Pixel3D pixel = null;
        int xmin0;
        int ymin0;
        int zmin0;
        int xmax0;
        int ymax0;
        int zmax0;

        xmin0 = getXmin();
        ymin0 = getYmin();
        zmin0 = getZmin();
        xmax0 = getXmax();
        ymax0 = getYmax();
        zmax0 = getZmax();

        for (int k = zmin0; k <= zmax0; k++) {
            for (int j = ymin0; j <= ymax0; j++) {
                for (int i = xmin0; i <= xmax0; i++) {
                    if (segImage.getPixel(i, j, k) == value) {
                        pixel = new Pixel3D(i, j, k, ima.getPix(i, j, k));
                        vector.add(pixel);
                    }
                }
            }
        }

        return vector;
    }

    /**
     * 
     * @param other object
     * @param dist_max in pixel between two contour points
     * @return nb ofcontours points below distance max to contours points in other object
     */
    public double pixelsContact(Object3D other, double dist_max) {
        int surf = 0;
        Pixel3D p0 = null;
        Pixel3D p1 = null;
        double dist;
        int s = contours.size();
        ArrayList othercontours = other.contours;
        int t = othercontours.size();
        double dmax2 = dist_max * dist_max;

        double[][] distres = new double[s][t];

        double dmin;
        int j0;

        for (int i = 0; i < s; i++) {
            IJ.showStatus("Computing contact surface 1/2 " + (100 * i) / s + "%");
            p0 = (Pixel3D) contours.get(i);
            dmin = dmax2;
            j0 = -1;
            for (int j = 0; j < t; j++) {
                distres[i][j] = -1;
                p1 = (Pixel3D) othercontours.get(j);
                dist = p0.distanceSquare(p1);
                if (dist <= dmin) {
                    dmin = dist;
                    j0 = j;
                }
            }
            if (j0 != -1) {
                distres[i][j0] = dmin;
                //System.out.println(""+i+" "+j0+" "+dmin);
            }
        }

        // count nb pix from 2 --> 1 having min
        surf = 0;
        for (int j = 0; j < t; j++) {
            IJ.showStatus("Computing contact surface 2/2 " + (100 * j) / t + "%");
            //System.out.println("j="+j);
            j0 = 0;
            while ((j0 < s) && (distres[j0][j]) == -1) {
                j0++;
            }
            if (j0 < s) {
                surf++;
            }
        }

        IJ.showStatus("Computing contact surfac 100%");

        return surf;
    }

    /**
     *  Computation of the inertia tensor with units value
     */
    public void computeMomentsInertia() {
        sxx = 0;
        sxy = 0;
        sxz = 0;
        syy = 0;
        syz = 0;
        szz = 0;
        for (int k = zmin; k <= zmax; k++) {
            for (int j = ymin; j <= ymax; j++) {
                for (int i = xmin; i <= xmax; i++) {
                    if (segImage.getPixel(i, j, k) == value) {
                        sxx += resXY * resXY * (j - by) * (j - by) + resZ * resZ * (k - bz) * (k - bz);
                        syy += resXY * resXY * (i - bx) * (i - bx) + resZ * resZ * (k - bz) * (k - bz);
                        szz += resXY * resXY * (i - bx) * (i - bx) + resXY * resXY * (j - by) * (j - by);
                        sxy += resXY * resXY * (i - bx) * (j - by);
                        sxz += resXY * resZ * (i - bx) * (k - bz);
                        syz += resXY * resZ * (j - by) * (k - bz);
                    }
                }
            }
        }
    }

    /**
     *  Computation of the dispersion tensor with units value
     */
    public void computeMoments() {
        sxx = 0;
        sxy = 0;
        sxz = 0;
        syy = 0;
        syz = 0;
        szz = 0;

        for (int k = zmin; k <= zmax; k++) {
            for (int j = ymin; j <= ymax; j++) {
                for (int i = xmin; i <= xmax; i++) {
                    if (segImage.getPixel(i, j, k) == value) {
                        sxx += (i - bx) * (i - bx);
                        syy += (j - by) * (j - by);
                        szz += (k - bz) * (k - bz);
                        sxy += (i - bx) * (j - by);
                        sxz += (i - bx) * (k - bz);
                        syz += (j - by) * (k - bz);
                    }
                }
            }
        }
        // resolution
        sxx *= resXY * resXY;
        syy *= resXY * resXY;
        szz *= resZ * resZ;
        sxy *= resXY * resXY;
        sxz *= resXY * resZ;
        syz *= resXY * resZ;
        // normalize by volume
        sxx /= volume;
        syy /= volume;
        szz /= volume;
        sxy /= volume;
        sxz /= volume;
        syz /= volume;

        eigen = null;
    }

    /**
     *  compute mass center with an image
     *
     * @param  ima  the image
     */
    private void computeMassCenter(Image3D ima) {
        if (ima != null) {
            cx = 0;
            cy = 0;
            cz = 0;
            double sum = 0;
            double sum2 = 0;
            double pix;
            double pmin = Double.MAX_VALUE;
            double pmax = Double.MIN_VALUE;
            for (int k = zmin; k <= zmax; k++) {
                for (int j = ymin; j <= ymax; j++) {
                    for (int i = xmin; i <= xmax; i++) {
                        if (segImage.getPixel(i, j, k) == value) {
                            pix = ima.getPix(i, j, k);
                            cx += i * pix;
                            cy += j * pix;
                            cz += k * pix;
                            sum += pix;
                            sum2 += pix * pix;
                            if (pix > pmax) {
                                pmax = pix;
                            }
                            if (pix < pmin) {
                                pmin = pix;
                            }
                        }
                    }
                }
            }
            cx /= sum;
            cy /= sum;
            cz /= sum;

            integratedDensity = sum;

            pixmin = pmin;
            pixmax = pmax;

            // standard dev
            int vol = getVolumePixels();
            sigma = Math.sqrt((sum2 - ((sum * sum) / vol)) / (vol - 1));
        }
    }

    /**
     *  Compute the barycenter and the volume
     */
    public final void computeCenter() {
        bx = 0;
        by = 0;
        bz = 0;
        int sum = 0;

        for (int k = zmin; k <= zmax; k++) {
            for (int j = ymin; j <= ymax; j++) {
                for (int i = xmin; i <= xmax; i++) {
                    if (segImage.getPixel(i, j, k) == value) {
                        bx += i;
                        by += j;
                        bz += k;
                        sum++;
                    }
                }
            }
        }
        bx /= sum;
        by /= sum;
        bz /= sum;

        volume = sum;
    }

    /**
     *  Compute the contours of the object rad=0.5
     */
    public void computeContours() {
        area = 0;
        contours = new ArrayList();

        //IJ.log("compute contours");

        final List list = Collections.synchronizedList(new ArrayList());

        final AtomicInteger ai = new AtomicInteger(zmin);
        Thread[] threads = ThreadUtil.createThreadArray();

        for (int ithread = 0; ithread < threads.length; ithread++) {
            threads[ithread] = new Thread() {

                @Override
                public void run() {
                    int pix0, pix1, pix2, pix3, pix4, pix5, pix6;
                    for (int k = ai.getAndIncrement(); k <= zmax; k = ai.getAndIncrement()) {
                        IJ.showStatus("Contours " + (100 * (k - zmin) / (zmax - zmin + 1)) + " % ");
                        for (int j = ymin; j <= ymax; j++) {
                            for (int i = xmin; i <= xmax; i++) {
                                pix0 = segImage.getPixel(i, j, k);
                                if (pix0 == value) {
                                    pix1 = segImage.getPixel(i + 1, j, k);
                                    pix2 = segImage.getPixel(i - 1, j, k);
                                    pix3 = segImage.getPixel(i, j + 1, k);
                                    pix4 = segImage.getPixel(i, j - 1, k);
                                    pix5 = segImage.getPixel(i, j, k + 1);
                                    pix6 = segImage.getPixel(i, j, k - 1);
                                    if ((pix1 != value) || (pix2 != value) || (pix3 != value) || (pix4 != value) || (pix5 != value) || (pix6 != value)) {
                                        area++;
                                        Pixel3D temp = new Pixel3D(i, j, k, value);
                                        list.add(temp);
                                    }
                                }
                            }
                        }
                    }
                }
            };
        }
        ThreadUtil.startAndJoin(threads);
        //IJ.log("size list=" + list.size());
        contours.addAll(list);
    }

    // The contours is given in an image (see OC3D)
    /**
     *
     * @param ima
     */
    public void computeContours(IntImage3D ima) {
        area = 0;
        contours = new ArrayList();
        for (int k = zmin; k <= zmax; k++) {
            IJ.showStatus("Contours " + (100 * (k - zmin) / (zmax - zmin + 1)) + " % ");
            for (int j = ymin; j <= ymax; j++) {
                for (int i = xmin; i <= xmax; i++) {
                    if (ima.getPixel(i, j, k) == value) {
                        area++;
                        Pixel3D temp = new Pixel3D(i, j, k, value);
                        contours.add(temp);
                    }
                }
            }
        }
    }

    /**
     *  Compute the bounding box of the object
     */
    public final void computeBounding() {
        xmin = segImage.getSizex();
        xmax = 0;
        ymin = segImage.getSizey();
        ymax = 0;
        zmin = segImage.getSizez();
        zmax = 0;
        for (int k = 0; k < segImage.getSizez(); k++) {
            for (int j = 0; j < segImage.getSizey(); j++) {
                for (int i = 0; i < segImage.getSizex(); i++) {
                    if (segImage.getPixel(i, j, k) == value) {
                    
                    	alObject.add(new VoxelObject(i, j, k));
                    	
                        if (i < xmin) {
                            xmin = i;
                        }
                        if (i > xmax) {
                            xmax = i;
                        }
                        if (j < ymin) {
                            ymin = j;
                        }
                        if (j > ymax) {
                            ymax = j;
                        }
                        if (k < zmin) {
                            zmin = k;
                        }
                        if (k > zmax) {
                            zmax = k;
                        }
                    }
                }
            }
        }
    }

    /**
     *  Gets the main axes vectors as a matrix
     *
     * @return    The matrix, e1 is main axis
     */
    public Matrix getMatrixAxes() {
        computeEigen();

        // swap e1 and e3 because e3 main axis
        Matrix M = (eigen.getV()).copy();
        double tmp;
        // swap e1 and e3
        tmp = M.get(0, 2);
        M.set(0, 2, M.get(0, 0));
        M.set(0, 0, tmp);
        tmp = M.get(1, 2);
        M.set(1, 2, M.get(1, 0));
        M.set(1, 0, tmp);
        tmp = M.get(2, 2);
        M.set(2, 2, M.get(2, 0));
        M.set(2, 0, tmp);

        return M;
    }

    /**
     *  Gets the Axis attribute of the Object3D object (unit)
     *
     * @param  order  the order of the axis
     * @return        The mainAxis value
     */
    public Vector3D getVectorAxis(int order) {
        computeEigen();

        Matrix evect = eigen.getV();

        Vector3D res = new Vector3D(evect.get(0, order), evect.get(1, order), evect.get(2, order));

        return res;
    }

    /**
     *  Constructor for the computeEigenInertia object
     */
    private void computeEigenInertia() {
        if (eigen == null) {
            Matrix mat = new Matrix(3, 3);
            mat.set(0, 0, sxx);
            mat.set(0, 1, -sxy);
            mat.set(0, 2, -sxz);
            mat.set(1, 0, -sxy);
            mat.set(1, 1, syy);
            mat.set(1, 2, -syz);
            mat.set(2, 0, -sxz);
            mat.set(2, 1, -syz);
            mat.set(2, 2, szz);

            eigen = new EigenvalueDecomposition(mat);
        }
    }

    /**
     *  Constructor for the computeEigen object
     */
    private void computeEigen() {
        if (eigen == null) {
            Matrix mat = new Matrix(3, 3);
            if (sxx == -1) {
                computeMoments();
            }
            mat.set(0, 0, sxx);
            mat.set(0, 1, sxy);
            mat.set(0, 2, sxz);
            mat.set(1, 0, sxy);
            mat.set(1, 1, syy);
            mat.set(1, 2, syz);
            mat.set(2, 0, sxz);
            mat.set(2, 1, syz);
            mat.set(2, 2, szz);

            eigen = new EigenvalueDecomposition(mat);
        }
    }

    /**
     *  Gets the valueAxis attribute of the Object3D object (unit)
     *
     * @param  order  the order of the value
     * @return        The valueAxis value
     */
    public double getValueAxis(int order) {
        computeEigen();

        double[] evalues = eigen.getRealEigenvalues();

        return evalues[order];
    }

    /**
     *  Gets the mainAxis attribute of the Object3D object (unit)
     *
     * @return    The mainAxis value
     */
    public Vector3D getMainAxis() {
        return getVectorAxis(2);
    }

    /**
     *  Gets the elongationAxis attribute of the Object3D object (unit)
     *
     * @return    The main elongationAxis value
     */
    public double getMainElongationAxis() {
        // ratio between high and median value
        return Math.sqrt((getValueAxis(2) / getValueAxis(1)));
    }

    /**
     *  Gets the elongationAxis attribute of the Object3D object (unit)
     *
     * @return    The main elongationAxis value
     */
    public double getMedianElongationAxis() {
        // ratio between median and low value
        return Math.sqrt((getValueAxis(1) / getValueAxis(0)));
    }

    /**
     *  Segment an object from a seed
     *
     * @param  seuil     Threshold to connect pixels
     * @param  val       value of the object
     * @param  xdep      x coordinate of the seed
     * @param  ydep      y coordinate of the seed
     * @param  zdep      z coordinate of the seed
     * @param originalImage original image with the values
     * @return           true if object cold be segmented
     */
    public boolean segmentSpot(int xdep, int ydep, int zdep, int seuil, int val, IntImage3D originalImage) {
        boolean changement = true;
        int xfin = xdep + 1;
        int yfin = ydep + 1;
        int zfin = zdep + 1;
        int sens = 1;
        int baryx = xdep;
        int baryy = ydep;
        int baryz = zdep;
        value = val;
        segImage.putPixel(xdep, ydep, zdep, value);
        volume++;

        ArrayList tmp = new ArrayList();

        int i;
        int j;
        int k;
        int l;
        int m;
        int n;

        IntImage3D original = originalImage;

        while (changement) {
            changement = false;
            for (k = sens == 1 ? zdep : zfin; ((sens == 1 && k <= zfin) || (sens == -1 && k >= zdep)); k += sens) {
                for (j = sens == 1 ? ydep : yfin; ((sens == 1 && j <= yfin) || (sens == -1 && j >= ydep)); j += sens) {
                    for (i = sens == 1 ? xdep : xfin; ((sens == 1 && i <= xfin) || (sens == -1 && i >= xdep)); i += sens) {
                        if (segImage.getPixel(i, j, k) == value) {
                            for (n = k - 1; n < k + 2; n++) {
                                for (m = j - 1; m < j + 2; m++) {
                                    for (l = i - 1; l < i + 2; l++) {
                                        if ((segImage.getPixel(l, m, n) == 0) && (original.getPixel(l, m, n) > seuil)) {
                                            segImage.putPixel(l, m, n, value);
                                            // original.putPixel(l, m, n, 0);
                                            baryx += l;
                                            baryy += m;
                                            baryz += n;
                                            volume++;
                                            if (l < xdep) {
                                                xdep--;
                                            }
                                            if (l > xfin) {
                                                xfin++;
                                            }
                                            if (m < ydep) {
                                                ydep--;
                                            }
                                            if (m > yfin) {
                                                yfin++;
                                            }
                                            if (n < zdep) {
                                                zdep--;
                                            }
                                            if (n > zfin) {
                                                zfin++;
                                            }
                                            changement = true;
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
            }
            sens *= -1;
        }
        xmin = xdep;
        ymin = ydep;
        zmin = zdep;
        xmax = xfin;
        ymax = yfin;
        zmax = zfin;
        bx = (float) baryx / (float) volume;
        by = (float) baryy / (float) volume;
        bz = (float) baryz / (float) volume;
        for (i = xmin; i <= xmax; i++) {
            for (j = ymin; j <= ymax; j++) {
                for (k = zmin; k <= zmax; k++) {
                    if (isContour(i, j, k)) {
                        area++;
                        Pixel3D temp = new Pixel3D(i, j, k, value);
                        tmp.add(temp);
                    }
                }
            }
        }
        // touch the borders of the image in x-Y
        touchBorders = false;
        if ((xmin <= 0) || (ymin <= 0)) {
            touchBorders = true;
        }
        if ((xmax >= original.getSizex() - 1) || (ymax >= original.getSizey() - 1)) {
            touchBorders = true;
        }
        if (debug && touchBorders) {
            IJ.log("touch" + value + " : " + xmin + " " + ymin + " " + zmin + " " + xmax + " " + ymax + " " + zmax);
        }

        contours = tmp;
        tmp = null;

        original = null;

        return true;
    }

    /**
     *  Segment an object from a seed, keep only max local
     *
     * @param  seuil     Threshold to connect pixels
     * @param  val       value of the object
     * @param  xdep      x coordinate of the seed
     * @param  ydep      y coordinate of the seed
     * @param  zdep      z coordinate of the seed
     * @param originalImage original image with the values
     * @return           true if object cold be segmented
     */
    public boolean segmentSpotMax(int xdep, int ydep, int zdep, int seuil, int val, IntImage3D originalImage) {
        boolean changement = true;
        int xfin = xdep + 1;
        int yfin = ydep + 1;
        int zfin = zdep + 1;
        int sens = 1;
        int baryx = xdep;
        int baryy = ydep;
        int baryz = zdep;
        value = val;
        segImage.putPixel(xdep, ydep, zdep, value);
        volume++;

        ArrayList tmp = new ArrayList();

        int i;
        int j;
        int k;
        int l;
        int m;
        int n;

        IntImage3D original = originalImage;
        int pixelCenter, pixel;

        while (changement) {
            changement = false;
            for (k = sens == 1 ? zdep : zfin; ((sens == 1 && k <= zfin) || (sens == -1 && k >= zdep)); k += sens) {
                for (j = sens == 1 ? ydep : yfin; ((sens == 1 && j <= yfin) || (sens == -1 && j >= ydep)); j += sens) {
                    for (i = sens == 1 ? xdep : xfin; ((sens == 1 && i <= xfin) || (sens == -1 && i >= xdep)); i += sens) {
                        if (segImage.getPixel(i, j, k) == value) {
                            pixelCenter = original.getPixel(i, j, k);
                            for (n = k - 1; n < k + 2; n++) {
                                for (m = j - 1; m < j + 2; m++) {
                                    for (l = i - 1; l < i + 2; l++) {
                                        if ((segImage.getPixel(l, m, n) == 0) && (original.getPixel(l, m, n) > seuil) && (original.getPixel(l, m, n) <= pixelCenter)) {
                                            segImage.putPixel(l, m, n, value);
                                            //original.putPixel(l, m, n, 0);
                                            baryx += l;
                                            baryy += m;
                                            baryz += n;
                                            volume++;
                                            if (l < xdep) {
                                                xdep--;
                                            }
                                            if (l > xfin) {
                                                xfin++;
                                            }
                                            if (m < ydep) {
                                                ydep--;
                                            }
                                            if (m > yfin) {
                                                yfin++;
                                            }
                                            if (n < zdep) {
                                                zdep--;
                                            }
                                            if (n > zfin) {
                                                zfin++;
                                            }
                                            changement = true;
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
            }
            sens *= -1;
        }
        xmin = xdep;
        ymin = ydep;
        zmin = zdep;
        xmax = xfin;
        ymax = yfin;
        zmax = zfin;
        bx = (float) baryx / (float) volume;
        by = (float) baryy / (float) volume;
        bz = (float) baryz / (float) volume;
        for (i = xmin; i <= xmax; i++) {
            for (j = ymin; j <= ymax; j++) {
                for (k = zmin; k <= zmax; k++) {
                    if (isContour(i, j, k)) {
                        area++;
                        Pixel3D temp = new Pixel3D(i, j, k, value);
                        tmp.add(temp);
                    }
                }
            }
        }
        // touch the borders of the image in x-Y
        touchBorders = false;
        if ((xmin <= 0) || (ymin <= 0)) {
            touchBorders = true;
        }
        if ((xmax >= original.getSizex() - 1) || (ymax >= original.getSizey() - 1)) {
            touchBorders = true;
        }
        if (debug && touchBorders) {
            IJ.log("touch" + value + " : " + xmin + " " + ymin + " " + zmin + " " + xmax + " " + ymax + " " + zmax);
        }

        contours = tmp;
        tmp = null;

        original = null;

        return true;
    }

    /**
     *  Description of the Method
     *
     * @param  obj  Description of the Parameter
     * @param  col  Description of the Parameter
     */
    public void draw(ObjectCreator3D obj, int col) {
        for (int z = zmin; z <= zmax; z++) {
            for (int y = ymin; y <= ymax; y++) {
                for (int x = xmin; x <= xmax; x++) {
                    if (segImage.getPixel(x, y, z) == value) {
                        obj.createPixel(x, y, z, col);
                    }
                }
            }
        }
    }

    /**
     *  Constructor for the createRoi object
     *
     * @param  z  Description of the Parameter
     * @return    Description of the Return Value
     */
    public Roi createRoi(int z) {
        // IJ.write("create roi " + z);
        int sx = segImage.getSizex();
        int sy = segImage.getSizey();
        ByteProcessor mask = new ByteProcessor(sx, sy);
        // object black on white
        //mask.invert();
        draw(mask, z, 255);
        ImagePlus maskPlus = new ImagePlus("mask " + z, mask);
        //maskPlus.show();
        //IJ.run("Create Selection");
        ThresholdToSelection tts = new ThresholdToSelection();
        tts.setup("", maskPlus);
        tts.run(mask);
        maskPlus.updateAndDraw();
        // IJ.write("sel=" + maskPlus.getRoi());
        //maskPlus.hide();

        return maskPlus.getRoi();
    }

    /**
     *  Description of the Method
     *
     * @param  mask  Description of the Parameter
     * @param  z     Description of the Parameter
     * @param  col   Description of the Parameter
     */
    public void draw(ByteProcessor mask, int z, int col) {
        for (int x = xmin; x <= xmax; x++) {
            for (int y = ymin; y <= ymax; y++) {
                if (segImage.getPixel(x, y, z) == value) {
                    mask.putPixel(x, y, col);
                }
            }
        }
    }

    /**
     *
     * @param mask
     * @param col
     */
    public void draw(IntImage3D mask, int col) {
        for (int z = zmin; z <= zmax; z++) {
            for (int x = xmin; x <= xmax; x++) {
                for (int y = ymin; y <= ymax; y++) {
                    if (segImage.getPixel(x, y, z) == value) {
                        mask.putPixel(x, y, z, col);
                    }
                }
            }
        }
    }

    /**
     *
     * @param ima
     * @param col
     */
    public void drawContours(ObjectCreator3D ima, int col) {
        int s = contours.size();
        Pixel3D p2;
        for (int j = 0; j < s; j++) {
            p2 = (Pixel3D) contours.get(j);
            ima.createPixel((int) p2.getX(), (int) p2.getY(), (int) p2.getZ(), col);
        }
    }

    /**
     *
     * @param mask
     * @param red
     * @param green
     * @param blue
     */
    public void draw(ImageStack mask, int red, int green, int blue) {
        ImageProcessor tmp;
        Color col = new Color(red, green, blue);
        for (int z = zmin; z <= zmax; z++) {
            tmp = mask.getProcessor(z + 1);
            tmp.setColor(col);
            for (int x = xmin; x <= xmax; x++) {
                for (int y = ymin; y <= ymax; y++) {
                    if (segImage.getPixel(x, y, z) == value) {
                        tmp.drawPixel(x, y);
                    }
                }
            }
        }
    }

    /**
     *
     * @param mask
     * @param other
     * @param red
     * @param green
     * @param blue
     */
    public void drawIntersection(ImageStack mask, Object3D other, int red, int green, int blue) {
        ImageProcessor tmp;
        IntImage3D otherSeg = other.getSegImage();
        int otherValue = other.getValue();
        Color col = new Color(red, green, blue);
        for (int z = zmin; z <= zmax; z++) {
            tmp = mask.getProcessor(z + 1);
            tmp.setColor(col);
            for (int x = xmin; x <= xmax; x++) {
                for (int y = ymin; y <= ymax; y++) {
                    if ((segImage.getPixel(x, y, z) == value) && (otherSeg.getPixel(x, y, z) == otherValue)) {
                        tmp.drawPixel(x, y);
                    }
                }
            }
        }
    }

    /**
     *
     * @param mask
     * @param col
     */
    public void draw(ImageStack mask, int col) {
        ImageProcessor tmp;
        for (int z = zmin; z <= zmax; z++) {
            tmp = mask.getProcessor(z + 1);
            for (int x = xmin; x <= xmax; x++) {
                for (int y = ymin; y <= ymax; y++) {
                    if (segImage.getPixel(x, y, z) == value) {
                        tmp.putPixel(x, y, col);
                    }
                }
            }
        }
    }

    /**
     *
     * @param mask
     * @param other
     * @param col
     */
    public void drawIntersection(ImageStack mask, Object3D other, int col) {
        ImageProcessor tmp;
        IntImage3D otherSeg = other.getSegImage();
        int otherValue = other.getValue();
        for (int z = zmin; z <= zmax; z++) {
            tmp = mask.getProcessor(z + 1);
            for (int x = xmin; x <= xmax; x++) {
                for (int y = ymin; y <= ymax; y++) {
                    if ((segImage.getPixel(x, y, z) == value) && (otherSeg.getPixel(x, y, z) == otherValue)) {
                        tmp.putPixel(x, y, col);
                    }
                }
            }
        }
    }

    /**
     *
     * @param ori
     * @param val
     * @param seuil
     * @param f
     * @param f1
     * @param f2
     * @param dist
     * @param p1
     * @param p2
     * @return
     */
    public boolean separeSpot2D(IntImage3D ori, int val, int seuil, Object3D f, Object3D f1, Object3D f2, double dist, int p1, int p2) {
        IntImage3D proj = ori.projectionZ(-1, p1, p2);
        //proj = (IntImage3D) proj.medianFilter(1, 1, 0);
        IntImage3D maxi = (IntImage3D) proj.createLocalMaximaImage(2, 2, 0, false);
        IntImage3D seg = f.getSegImage();
        if (debug) {
            System.out.println("Separe2D " + val);
        }
        IntImage3D seg2D = seg.projectionZ(val, p1, p2);
        double cx1 = 0;
        double cy1 = 0;
        double cz1 = 0;
        double nb1 = 0;
        double cx2 = 0;
        double cy2 = 0;
        double cz2 = 0;
        double nb2 = 0;
        double dist1 = 0;
        double dist2 = 0;
        int nb = 0;
        int k;

        if (debug) {
            System.out.println("separe spots 2D : " + val);
        }

        // compter nb de max locaux
        for (int j = 2; j < maxi.getSizey() - 2; j++) {
            for (int i = 2; i < maxi.getSizex() - 2; i++) {
                if ((maxi.getPixel(i, j, 0) > 0) && (seg2D.getPixel(i, j, 0) > 0)) {
                    maxi.putPixel(i, j, 0, 255);
                    nb++;
                } else {
                    maxi.putPixel(i, j, 0, 0);
                }
            }
        }
        if (debug) {
            new ImagePlus("max loco-" + val, maxi.getStack()).show();
            new ImagePlus("seg2D-" + val, seg2D.getStack()).show();
            new ImagePlus("seg-" + val, seg.getStack()).show();
            new ImagePlus("proj-" + val, proj.getStack()).show();
        }

        // si 1 seul pixel on retourne false
        if (nb < 2) {
            return false;
        }

        Pixel3D tablo[] = new Pixel3D[nb];
        nb = 0;
        // ranger max locaux dans le tablo
        for (int j = 2; j < maxi.getSizey() - 2; j++) {
            for (int i = 2; i < maxi.getSizex() - 2; i++) {
                if ((maxi.getPixel(i, j, 0) > 0)) {
                    tablo[nb] = new Pixel3D(i, j, 0, 0);
                    nb++;
                }
            }
        }

        // chercher deux max locaux les plus eloignes
        int i1 = 0;
        int i2 = 0;
        double dmax = 0.0;
        double d;
        for (int i = 0; i < nb; i++) {
            for (int j = i + 1; j < nb; j++) {
                d = tablo[i].dist(tablo[j]);
                if (d > dmax) {
                    dmax = d;
                    i1 = i;
                    i2 = j;
                }
            }
        }

        // Ranger les max locaux en deux classes et calculer barycentre des deux classes
        cx1 = 0;
        cy1 = 0;
        cz1 = 0;
        cx2 = 0;
        cy2 = 0;
        cz2 = 0;
        double d1;
        double d2;
        if (debug) {
            System.out.println("max locaux eloignes:");
            System.out.println("centre1: " + tablo[i1].getX() + " " + tablo[i1].getY() + " " + tablo[i1].getZ());
            System.out.println("centre2: " + tablo[i2].getX() + " " + tablo[i2].getY() + " " + tablo[i2].getZ());
            System.out.println("distance : " + tablo[i1].dist(tablo[i2]));
        }
        Pixel3D PP1 = new Pixel3D(tablo[i1].getX(), tablo[i1].getY(), tablo[i1].getZ(), 0.0);
        Pixel3D PP2 = new Pixel3D(tablo[i2].getX(), tablo[i2].getY(), tablo[i2].getZ(), 0.0);
        Pixel3D P1 = new Pixel3D((int) cx1, (int) cy1, (int) cz1, 0);
        Pixel3D P2 = new Pixel3D((int) cx2, (int) cy2, (int) cz2, 0);

        int nbite = 0;
        while (((P1.dist(PP1) > 1) || (P2.dist(PP2) > 1)) && (nbite < 10)) {
            nbite++;
            cx1 = 0;
            cy1 = 0;
            cx2 = 0;
            cy2 = 0;
            nb1 = 0;
            nb2 = 0;
            P1.setX(PP1.getX());
            P1.setY(PP1.getY());
            P2.setX(PP2.getX());
            P2.setY(PP2.getY());

            for (int i = 0; i < nb; i++) {
                d1 = P1.dist(tablo[i]);
                d2 = P2.dist(tablo[i]);
                if (d1 < d2) {
                    cx1 += tablo[i].getX();
                    cy1 += tablo[i].getY();
                    nb1++;
                } else {
                    cx2 += tablo[i].getX();
                    cy2 += tablo[i].getY();
                    nb2++;
                }
            }
            cx1 /= nb1;
            cy1 /= nb1;
            cx2 /= nb2;
            cy2 /= nb2;

            PP1.setX((int) cx1);
            PP1.setY((int) cy1);
            PP2.setX((int) cx2);
            PP2.setY((int) cy2);
        }

        if (debug) {
            System.out.println("max locaux centres:");
            System.out.println("centre1: " + cx1 + " " + cy1 + " " + cz1);
            System.out.println("centre2: " + cx2 + " " + cy2 + " " + cz2);
            System.out.println("distance: " + PP1.dist(PP2));
        }

        // remonter centres en 3D
        // prendre z milieu
        int k1;
        int k2;
        k = f.getZmin();
        int sizez = this.getSegImage().getSizez();
        int sizey = this.getSegImage().getSizey();
        int sizex = this.getSegImage().getSizex();
        while ((seg.getPixel((int) cx1, (int) cy1, k) != val) && (k < sizez)) {
            k++;
        }
        if (k >= sizez) {
            k1 = f.getZmin();
            k2 = f.getZmax();
        } else {
            k1 = k;
            while ((seg.getPixel((int) cx1, (int) cy1, k) == val) && (k < sizez)) {
                k++;
            }
            if (k >= sizez) {
                k2 = f.getZmax();
            } else {
                k2 = k;
            }
        }
        cz1 = (k1 + k2) / 2.0;

        // z pour spot 2
        k = f.getZmin();
        while ((seg.getPixel((int) cx2, (int) cy2, k) != val) && (k < sizez)) {
            k++;
        }
        if (k >= sizez) {
            k1 = f.getZmin();
            k2 = f.getZmax();
        } else {
            k1 = k;
            while ((seg.getPixel((int) cx2, (int) cy2, k) == val) && (k < sizez)) {
                k++;
            }
            if (k >= sizez) {
                k2 = f.getZmax();
            } else {
                k2 = k;
            }
        }
        cz2 = (k1 + k2) / 2.0;

        PP1.setZ((int) cz1);
        PP2.setZ((int) cz2);

        if (debug) {
            System.out.println("max locaux centres en Z:");
            System.out.println("centre1: " + cx1 + " " + cy1 + " " + cz1);
            System.out.println("centre2: " + cx2 + " " + cy2 + " " + cz2);
            System.out.println("distance: " + PP1.dist(PP2));
        }

        // si distance trop petite, on retourne false
        if (PP1.dist(PP2) < dist) {
            return false;
        }

        // hybrid watershed test
        double db1 = f.distPixelBorder(PP1);
        double db2 = f.distPixelBorder(PP2);
        //System.out.println("db " + db1 + " " + db2);
        double db = db1 - db2;
        boolean oneIsLarger = true;
        // object 2 larger than object 1
        if (db2 > db1) {
            db = db2 - db1;
            oneIsLarger = false;
        }

        ////////////////////////////////////////////////////////
        ///// separate the two objects   ///////////////////////
        ////////////////////////////////////////////////////////
        //ArrayUtil tab;
        IntImage3D seg1 = new IntImage3D(sizex, sizey, sizez);
        IntImage3D seg2 = new IntImage3D(sizex, sizey, sizez);
        //IntImage3D tmp;
        int f1x = 0;
        int f1y = 0;
        int f1z = 0;
        int f2x = 0;
        int f2y = 0;
        int f2z = 0;
        for (k = 0; k < seg.getSizez(); k++) {
            for (int j = 0; j < seg.getSizey(); j++) {
                for (int i = 0; i < seg.getSizex(); i++) {
                    if (seg.getPixel(i, j, k) == val) {
                        dist1 = (i - cx1) * (i - cx1) + (j - cy1) * (j - cy1) + (k - cz1) * (k - cz1);
                        dist2 = (i - cx2) * (i - cx2) + (j - cy2) * (j - cy2) + (k - cz2) * (k - cz2);
                        if (oneIsLarger) {
                            dist1 -= db;
                        } else {
                            dist2 -= db;
                        }
                        if (dist1 < dist2) {
                            seg1.putPixel(i, j, k, 255);
                            f1x = i;
                            f1y = j;
                            f1z = k;
                        } else {
                            seg2.putPixel(i, j, k, 255);
                            f2x = i;
                            f2y = j;
                            f2z = k;
                        }
                    }
                }
            }
        }
        //int mini = seg.getMinAboveValue(0);
        int newVal = seg.getMaximum() + 1;
        if (debug) {
            System.out.println("newVal=" + newVal);
        }
        //tmp = new IntImage3D(sizex, sizey, sizez);
        f1.segmentSpot(f1x, f1y, f1z, 200, val, seg1);
        //seg1 = f1.getSegImage();
        //seg1 = (IntImage3D) tmp.closing3D(0, 1, 1, 1, 1);
        //seg1 = (IntImage3D) seg1.opening3D(0, 1, 1, 1, 1);

        //tmp = new IntImage3D(sizex, sizey, sizez);
        f2.segmentSpot(f2x, f2y, f2z, 200, newVal, seg2);
        //seg2 = f2.getSegImage();
        //seg2 = (IntImage3D) tmp.closing3D(0, 1, 1, 1, 1);
        //seg2 = (IntImage3D) seg2.opening3D(0, 1, 1, 1, 1);
        //seg.replacePixelsValue(val, 0);
        for (k = 0; k < seg.getSizez(); k++) {
            for (int j = 0; j < seg.getSizey(); j++) {
                for (int i = 0; i < seg.getSizex(); i++) {
                    if (seg1.getPixel(i, j, k) == 255) {
                        seg.putPixel(i, j, k, val);
                    }
                    if (seg2.getPixel(i, j, k) == 255) {
                        seg.putPixel(i, j, k, newVal);
                    }
                }
            }
        }
        f1.setValue(val);
        f1.computeBounding();
        f1.computeCenter();
        f1.computeContours();
        f2.setValue(newVal);
        f2.computeBounding();
        f2.computeCenter();
        f2.computeContours();
        if (debug) {
            new ImagePlus("seg1-" + val, seg1.getStack()).show();
            new ImagePlus("seg2-" + val, seg2.getStack()).show();
            new ImagePlus("seg-" + val, seg.getStack()).show();
        }
        return (nb2 > 0);
    }

    /**
     *  Display information
     *
     * @return    text
     */
    @Override
    public String toString() {
        return ("Object3D : " + value + " (" + (int) (bx + 0.5) + "," + (int) (by + 0.5) + "," + (int) (bz + 0.5) + ")");
    }

    // not used only for library link problem
    /**
     * 
     * @param threads
     */
    public static void startAndJoin(Thread[] threads) {
        for (int ithread = 0; ithread < threads.length; ++ithread) {
            threads[ithread].setPriority(Thread.NORM_PRIORITY);
            threads[ithread].start();
        }

        try {
            for (int ithread = 0; ithread < threads.length; ++ithread) {
                threads[ithread].join();
            }
        } catch (InterruptedException ie) {
            throw new RuntimeException(ie);
        }
    }
    
    /**
     * return the ArrayList of Voxels of this object
     */
    public ArrayList<VoxelObject> getObject(){
		return alObject;
    }
    /**
     * 
     * Object Voxel
     * @param x,y,z cordonate in ImageStack 
     */
    class VoxelObject{
		int x,y,z;
		VoxelObject(int x,int y,int z){
			this.x=x;
			this.y=y;
			this.z=z;
		}

		int getX(){
			return x;
		}
		int getY(){
			return y;
		}
		int getZ(){
			return z;
		}
	}
}
